using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using Betfair.Api.Responses;
using Betfair.Api.Types;
using log4net;

namespace Betfair.Api
{
    internal sealed class BetfairProxyImpl : IBetfairProxy
    {
        private readonly ILog _logger = LogManager.GetLogger(typeof(BetfairProxyImpl));

        private readonly string _requestUrl;
        private readonly string _loginUrl;
        private ProxyBuilder _proxyBuilder;
        private string _applicationKey;
        private Session _session;

        public BetfairProxyImpl(string loginUrl, string requestUrl)
        {
            _loginUrl = loginUrl;
            _requestUrl = requestUrl;
        }

        public async Task<Response> Login(string username, string password, string applicationKey, string certificatePath, string certificatePassword)
        {
            try
            {
                _applicationKey = applicationKey;
                _proxyBuilder = new ProxyBuilder(_requestUrl, applicationKey);
                _session = await new WebHelper().Seturl(string.Concat(_loginUrl, "certlogin"))
                            .SetApplicationKey(applicationKey)
                            .SetCertificate(certificatePath, certificatePassword)
                            .SetContentType(false)
                            .SetDataToPost(string.Format("username={0}&password={1}", username, password))
                            .Build<Session>();
                return _session.Status == "SUCCESS" ? new Response() : new Response(ResponseType.ERROR, _session.Status);
            }
            catch (WebException e)
            {
                _logger.Error(e);
                return new Response(ResponseType.ERROR, e.Message);
            }
            catch (Exception e)
            {
                _logger.Error(e);
                return new Response(ResponseType.ERROR, e.Message);
            }
        }

        public async Task<Response<KeepAlive>> KeepAlive()
        {
            try
            {
                Response<KeepAlive> checkSession = CheckSession<KeepAlive>();
                if (checkSession.ResponseType != ResponseType.OK)
                    return checkSession;
                return new Response<KeepAlive>(
                        result: await new WebHelper().Seturl(string.Concat(_loginUrl, "keepAlive"))
                                    .SetApplicationKey(_applicationKey)
                                    .SetSessionToken(_session.Token)
                                    .Build<KeepAlive>());
            }
            catch (Exception e)
            {
                _logger.Error(e);
                return new Response<KeepAlive>(ResponseType.ERROR, error: e.Message);
            }
        }

        public async Task<Response<IList<EventType>>> GetEventTypes(int id, Params parameters)
        {
            try
            {
                Response<IList<EventType>> checkSession = CheckSession<IList<EventType>>();
                if (checkSession.ResponseType != ResponseType.OK)
                    return checkSession;
                BetfairResponse<IList<EventTypeResponse>> betfairResponse =
                    await
                        _proxyBuilder.Build<IList<EventTypeResponse>>(_session.Token, "listEventTypes", parameters, id);
                return
                    new Response<IList<EventType>>(
                        result:
                            betfairResponse.Result.Select(item => item.EventType).OrderBy(item => item.Name).ToList());
            }
            catch (Exception e)
            {
                _logger.Error(e);
                return new Response<IList<EventType>>(ResponseType.ERROR, error: e.Message);
            }
        }

        public async Task<Response<IList<Competition>>> GetCompetions(int id, Params parameters)
        {
            try
            {
                Response<IList<Competition>> checkSession = CheckSession<IList<Competition>>();
                if (checkSession.ResponseType != ResponseType.OK)
                    return checkSession;
                BetfairResponse<IList<CompetitionResponse>> betfairResponse =
                    await
                        _proxyBuilder.Build<IList<CompetitionResponse>>(_session.Token, "listCompetitions", parameters,
                            id);
                return
                    new Response<IList<Competition>>(
                        result:
                            betfairResponse.Result.Select(item => item.Competition).OrderBy(item => item.Name).ToList());
            }
            catch (Exception e)
            {
                _logger.Error(e);
                return new Response<IList<Competition>>(ResponseType.ERROR, error: e.Message);
            }
        }

        public async Task<Response<IList<Country>>> GetCountries(int id, Params parameters)
        {
            try
            {
                Response<IList<Country>> checkSession = CheckSession<IList<Country>>();
                if (checkSession.ResponseType != ResponseType.OK)
                    return checkSession;
                BetfairResponse<IList<Country>> betfairResponse =
                    await _proxyBuilder.Build<IList<Country>>(_session.Token, "listCountries", parameters, id);
                return new Response<IList<Country>>(result: betfairResponse.Result.OrderBy(item => item.Code).ToList());
            }
            catch (Exception e)
            {
                _logger.Error(e);
                return new Response<IList<Country>>(ResponseType.ERROR, error: e.Message);
            }
        }

        public async Task<Response<IList<Event>>> GetEvents(int id, Params parameters)
        {
            try
            {
                Response<IList<Event>> checkSession = CheckSession<IList<Event>>();
                if (checkSession.ResponseType != ResponseType.OK)
                    return checkSession;
                BetfairResponse<IList<EventResponse>> betfairResponse =
                    await _proxyBuilder.Build<IList<EventResponse>>(_session.Token, "listEvents", parameters, id);
                return
                    new Response<IList<Event>>(
                        result: betfairResponse.Result.Select(item => item.Event).OrderBy(item => item.Name).ToList());
            }
            catch (Exception e)
            {
                _logger.Error(e);
                return new Response<IList<Event>>(ResponseType.ERROR, error: e.Message);
            }
        }

        public async Task<Response<IList<MarketType>>> GetMarketTypes(int id, Params parameters)
        {
            try
            {
                Response<IList<MarketType>> checkSession = CheckSession<IList<MarketType>>();
                if (checkSession.ResponseType != ResponseType.OK)
                    return checkSession;
                BetfairResponse<IList<MarketType>> betfairResponse =
                    await _proxyBuilder.Build<IList<MarketType>>(_session.Token, "listMarketTypes", parameters, id);
                return
                    new Response<IList<MarketType>>(result: betfairResponse.Result.OrderBy(item => item.Name).ToList());
            }
            catch (Exception e)
            {
                _logger.Error(e);
                return new Response<IList<MarketType>>(ResponseType.ERROR, error: e.Message);
            }
        }

        public async Task<Response<IList<MarketCatalogue>>> GetMarkets(int id, Params parameters)
        {
            try
            {
                Response<IList<MarketCatalogue>> checkSession = CheckSession<IList<MarketCatalogue>>();
                if (checkSession.ResponseType != ResponseType.OK)
                    return checkSession;
                BetfairResponse<IList<MarketCatalogue>> betfairResponse =
                    await
                        _proxyBuilder.Build<IList<MarketCatalogue>>(_session.Token, "listMarketCatalogue", parameters,
                            id);
                return
                    new Response<IList<MarketCatalogue>>(
                        result: betfairResponse.Result.OrderBy(item => item.Name).ToList());
            }
            catch (Exception e)
            {
                _logger.Error(e);
                return new Response<IList<MarketCatalogue>>(ResponseType.ERROR, error: e.Message);
            }
        }

        public async Task<Response<IList<MarketBook>>> GetMarketBook(int id, Params parameters)
        {
            try
            {
                Response<IList<MarketBook>> checkSession = CheckSession<IList<MarketBook>>();
                if (checkSession.ResponseType != ResponseType.OK)
                    return checkSession;
                BetfairResponse<IList<MarketBook>> betfairResponse =
                    await _proxyBuilder.Build<IList<MarketBook>>(_session.Token, "listMarketBook", parameters, id);
                return new Response<IList<MarketBook>>(result: betfairResponse.Result.ToList());
            }
            catch (Exception e)
            {
                _logger.Error(e);
                return new Response<IList<MarketBook>>(ResponseType.ERROR, error: e.Message);
            }
        }

        private Response<T> CheckSession<T>()
        {
            if (_session == null)
                return new Response<T>(ResponseType.ERROR, error: "You need to login first");
            if (_session.Status != "SUCCESS")
                return new Response<T>(ResponseType.ERROR, error: "Invalid credentials");
            return new Response<T>();
        }
    }
}